#include "initmeasurement.h"

InitMeasurement::InitMeasurement(QObject *parent) : QThread(parent)
{
    seed = replicate = method = numGerm = 0;
    caT1 = caT2 = 0;
    for(int i = 0; i < 6; i++) inter[i] = 0;

}

InitMeasurement::~InitMeasurement()
{
}

void InitMeasurement::addInfo(ImageLoader *loader, int s, int r, int m)
{
    if(loader)
        imgL = loader;
    else return;

    seed = s;
    replicate = r;
    method = m;

    currfile = imgL->getCurrFile();
    if(seed == 1)       // Light seeds
    {
        caT1 = 30;
        caT2 = 180;
        initfile = imgL->getFileByIndex(replicate - 1);
    }
    else if(seed = 2)   // Dark seeds
    {
        caT1 = 130;
        caT2 = 90;
        initfile = imgL->getFileByIndex(replicate + 3);
    }
}

void InitMeasurement::run()
{
    QString outfile = getOutFile();
    ofstream out(outfile.toStdString().c_str());

    int i=0, j=0;
    CvMat matInfo;

    if(out.is_open())
    {
        info = Mat(6, 16, CV_64FC1);
        matInfo = info;

        for(i = 0; i < 6; i++)
        {
            for(j = 0; j < 16; j++)
            {
                cvmSet(&matInfo, i, j, 0);
            }
        }

        if(seed == 1)               // Light-color seeds
        {
            inter[0] = 0;
            inter[1] = 3;
            inter[2] = 4;
            inter[3] = 7;
            inter[4] = 8;
            inter[5] = 11;
        }
        else if(seed == 2)          // Dark-color seeds
        {
            inter[0] = 1;
            inter[1] = 2;
            inter[2] = 5;
            inter[3] = 6;
            inter[4] = 9;
            inter[5] = 10;
        }

        process(out);
        count();
        qDebug() << seed << " , " << replicate << " , " << method << " done............";

    }

    out.close();
}


// After getting roi for initial image, then process
// each interval of this replicate. Here is an interval
// id to identify location of the replicate in the
// sequence. As of now, interval id's for light seeds are
// 3, 4, 7, 8, 11
void InitMeasurement::process(ofstream &out)
{
    Mat img, hsv, bin_img, ske_img, canny_img;
    vector<Mat> planes;

    QString file = "";
    int index = 0;

    for(int i = 0; i < 6; i++)
    {
        // File index of each replicate in image sequence
        index = inter[i]*4+replicate-1;
        // Get corresponding file name
        file = imgL->getFileByIndex(index);

        img = imread(file.toStdString(), -1);
        if(!img.data) continue;

        canny_img = Mat(img.size(), CV_8UC1);

        cvtColor(img, hsv, CV_BGR2HSV);
        split(hsv, planes);

        // Find edges in vue channel of HSV
        toCanny(planes[2], canny_img);
        // Find ROI for canny image of the first image of
        if (i == 0)
        {
            findROI(canny_img, roi, replicate, seed);
        }
        // Apply found ROI to vue channel
        planes[2] = Mat(planes[2], roi);

        // bin_img and ske_img will have the same size after
        // v has been set roi
        bin_img = planes[2].clone();
        ske_img = planes[2].clone();

        if(method ==1 || method == 2 || method == 4)
        {
            toBin(planes[2], bin_img, seed);
            toSkeleton(bin_img, ske_img);
            bitwise_not(ske_img, ske_img);
            getStat(ske_img, out, i);
        }
    }
}

void InitMeasurement::count()
{
    int index=0, group=0, rep=0;
    QString initfile = "";
    Mat init_img, curr_img;
    Rect roi;
    CvMat matInitial;

    if(currfile.length() > 0)
    {
        index = imgL->getCurrIndex();

        // 4 equals number of intervals
        group = index / 4;

        if(group == 0 || group == 1)
        {
            // pictures from initial group has been selected
            // no need to count germinated seeds
        }
        // These groups are for light seeds only
        else if(group==3 || group==4 || group==7 || group==8 || group==11)
        {
            rep = index % 4;
            initfile = imgL->getFileByIndex(rep);

            init_img = imread(initfile.toStdString(), -1);
            curr_img = imread(currfile.toStdString(), -1);

            initial = Mat(1, 16, CV_64FC1);
            matInitial = initial;

            for(int i = 0; i < 16; i++)
                cvmSet(&matInitial, 0, i, 0);

            getInitLength(init_img, rep+1, roi);
            getCurrLength(curr_img, roi);

        }
    }
}
void InitMeasurement::getInitLength(const Mat& img, int rep, Rect &roi)
{
    Mat hsv, bin_img, ske_img, canny_img;
    vector<Mat> planes;
    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;

    if(!img.data) return;

    canny_img = Mat(img.size(), CV_8UC1);

    cvtColor(img, hsv, CV_BGR2HSV);
    split(hsv, planes);
    toCanny(planes[2], canny_img);
    findROI(canny_img, roi, rep, seed);

    planes[2] = Mat(planes[2], roi);
    bin_img = Mat(planes[2].size(), CV_8UC1);
    ske_img = Mat(planes[2].size(), CV_8UC1);

    if(seed == 1)
    {
        // bin_img and ske_img will have the same size after
        // v has been set roi

        toBin(planes[2], bin_img, 1);
        toSkeleton(bin_img, ske_img);
        bitwise_not(ske_img, ske_img);

        findContours(ske_img, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);

    }
    else if(seed == 2)
    {
        findContours(canny_img, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
    }

    double arcL=0, value=0;
    Rect bbox;

    for(int idx = 0; idx >= 0; idx = hierarchy[idx][0])
    {
        arcL = arcLength(Mat(contours[idx]), -1);
        if(arcL > 200 )
        {
            bbox = boundingRect(Mat(contours[idx]));

            if(abs(bbox.x-ske_img.cols) < 20 ) continue;

            if(method == 1)
                value = arcL;
            else if(method == 2)
                value = bbox.width*bbox.height;
            else if(method == 4)
                value = bbox.height;

            addToMat(initial, 0, value, bbox);
        }
    }
}
void InitMeasurement::getCurrLength(const Mat& img, Rect &roi)
{
    Mat hsv, bin_img, ske_img, canny_img;
    vector<Mat> planes;
    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;

    if(!img.data) return;

    resultImg = img.clone();
    canny_img = Mat(img.size(), CV_8UC1);

    cvtColor(img, hsv, CV_BGR2HSV);
    split(hsv, planes);

    toCanny(planes[2], canny_img);

    planes[2] = Mat(planes[2], roi);
    resultImg = Mat(resultImg, roi);

    bin_img = Mat(planes[2].size(), CV_8UC1);
    ske_img = Mat(planes[2].size(), CV_8UC1);
    binImg = Mat(planes[2].size(), CV_8UC1);
    skeImg = Mat(planes[2].size(), CV_8UC1);
    grayImg = Mat(planes[2].size(), CV_8UC1);
    contourImg = Mat(planes[2].size(), CV_8UC3);

    if(seed == 1)
    {
        toBin(planes[2], bin_img, seed);
        toSkeleton(bin_img, ske_img);
        bitwise_not(ske_img, ske_img);

        findContours(ske_img, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
    }

    else if(seed == 2)
        findContours(canny_img, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);

    binImg = bin_img.clone();
    skeImg = ske_img.clone();
    grayImg = planes[2].clone();

    double arcL=0, value=0;
    Rect bbox;
    int diff=0;

    numGerm = 0;

    for(int idx = 0; idx >= 0; idx = hierarchy[idx][0])
    {
        arcL = arcLength(Mat(contours[idx]), -1);

        if(arcL > 200 )
        {
            bbox = boundingRect(Mat(contours[idx]));

            if(abs(bbox.x-ske_img.cols) < 20 ) continue;

            if(method == 1) value = arcL;
            else if(method == 2) value = bbox.width*bbox.height;
            else if(method == 4) value = bbox.height;

            diff = getDiff(value, bbox);

            if(diff > 0)
            {
                rectangle(resultImg,
                          Point(bbox.x, bbox.y),
                          Point(bbox.x+bbox.width, bbox.y+bbox.height),
                          CV_RGB(255, 0, 0), 2);

                drawContours(contourImg,
                             contours,
                             idx,
                             CV_RGB(0, 255, 0),
                             1,
                             8,
                             hierarchy);
                numGerm++;
            }
        }
    }
}

int InitMeasurement::getDiff(double value, Rect &box)
{
    int col=0;
    int x=box.x, y=box.y;
    double v=0;

    col = x / 200;

    CvMat mat = Mat(initial);

    if((x >= 1600) || (x >= 1400 && x <= 1450) || (x >= 1200 && x <= 1250) ||
            (x >= 1000 && x <= 1050) || (x >= 800 && x <= 850) ||
            (x >= 600 && x <= 650) || (x >= 400 && x <= 450) ||
            (x >= 200 && x <= 250)) col--;

    if(y >= 1000) col += 8;


    v = cvmGet(&mat, 0, col);

    return (value - v);
}
void InitMeasurement::toCanny(const Mat& src, Mat& dst)
{
    Mat temp = Mat(src.size(), src.type());
    Mat bg = Mat(src.size(), src.type());
    Mat sub = Mat(src.size(), src.type());

    Mat element1 = getStructuringElement(MORPH_ELLIPSE, Size(4, 4), Point(1, 1));
    Mat element2 = getStructuringElement(MORPH_RECT, Size(7,7), Point(1,1));

    dilate(src, temp, element1, Point(), 10);
    erode(temp, bg, element2, Point(), 10);
    bitwise_not(bg, bg);
    subtract(src, bg, sub);
    Canny(sub, dst, caT1, caT2, 3);
}
// Based on canny image 'src', find roi which only contains
// seeds that are inside the germination box
void InitMeasurement::findROI(Mat& src, Rect &roi, int replicate, int seed)
{
    vector<Vec2f> lines;

    HoughLines(src, lines, 1, CV_PI/180, 80, 30, 10);

    float x = 0, y = 0, angle = 0;

    for(size_t i = 0; i < lines.size(); i++)
    {
        float theta = lines[i][1];
        // Convert to degree
        angle = theta*180/CV_PI;

        if(abs(angle - 90) > 10) lines.erase(lines.begin() + i);
    }

    int min_x=lines[0][0]*cos(lines[0][1]), max_x=0;
    int mid=0, width=0;
    int diff = 0;

    for(size_t i = 1; i < lines.size(); i++)
    {
        mid = lines[i][0]*cos(lines[i][1]);

        if( mid > max_x)
            max_x = mid;
        if( mid < min_x)
            min_x = mid;
    }

    if(seed == 1)
    {
        if(replicate == 1 || replicate == 2)
            diff = 770;
        else if (replicate == 4)
            diff = 350;
    }
    else if (seed == 2)
    {
        diff = 350;
    }
    // Estimate value to get ROI
    if((max_x-min_x) > 25)
        width = max_x-min_x-diff;


    roi = Rect(min_x+200, 150, width, width*4/5);
    src = Mat(src, roi);

}
void InitMeasurement::toBin(const Mat& src, Mat& dst, int type)
{
    Mat temp;
    // After NOT operation, dark seeds will be transformed to the opposite
    // color which means seed coat will be turned to white and radicle if any
    // will be turned to dark color. Light seeds will be turned to dark color
    // even for radicle since radicle has similar color with seed coats
    bitwise_not(src, temp);

    if(type == 1)        // Light seeds
    {
        // If src image is "v", use threshold 50, if src image
        // is subtracted foreground
        //cvThreshold(temp, dst, 50, 255, CV_THRESH_BINARY);
        threshold(temp, dst, 50, 255, CV_THRESH_BINARY);
        bitwise_not(dst, dst);
    }
    else if(type == 2)   // Dark Seeds
    {

    }
}
void InitMeasurement::toSkeleton(const Mat& src, Mat& dst)
{    
    // Distance transform must be of type IPL_DEPTH_32F
    Mat dist = Mat(src.size(), CV_32F);
    Mat temp = Mat(src.size(), CV_32F);


    distanceTransform(src, dist, CV_DIST_L2, 3);
    Laplacian(dist, temp,  CV_32F);
    threshold(temp, temp, -1, 255, CV_THRESH_BINARY);

    temp.convertTo(dst, 8, 1, 0);
}
void InitMeasurement::getStat(Mat& src, ofstream &out, int row)
{
    Mat temp, temp_rgb;

    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;

    temp = src.clone();
    cvtColor(temp, temp_rgb, CV_GRAY2BGR);

    double arcL = 0, value = 0;
    Rect bbox;

    findContours(src, contours, hierarchy, 1, 2, Point());

    for(int idx = 0; idx >= 0; idx = hierarchy[idx][0])
    {
        bbox = boundingRect(Mat(contours[idx]));

        if(seed == 1) arcL = arcLength(Mat(contours[idx]), -1);
        else if(seed == 2) arcL = bbox.height;

        if(arcL > 200 )
        {
            if(abs(bbox.x-src.cols) < 20 ) continue;

            drawContours(temp_rgb,
                         contours,
                         idx,
                         CV_RGB(0, 255, 0),
                         1,
                         8,
                         hierarchy);


            rectangle(temp_rgb,
                      Point(bbox.x, bbox.y),
                      Point(bbox.x+bbox.width, bbox.y+bbox.height),
                      CV_RGB(0, 0, 255), 2);

            if(method == 1)
                value = arcL;
            else if(method == 2)
                value = bbox.width*bbox.height;
            else if(method == 4)
                value = bbox.height;


            out << value << "( " << bbox.x << " " << bbox.y <<"),";
            addToMat(info, row, value, bbox);

        }
    }
    out << "\n\n";
}
void InitMeasurement::addToMat(const Mat& info, int row, double value, Rect &box)
{
    int col=0;
    double curr=0;
    int x = box.x;
    int y = box.y;
    CvMat matInfo = info;
    col = x / 200;

    if((x >= 1600) || (x >= 1400 && x <= 1450) || (x >= 1200 && x <= 1250) ||
            (x >= 1000 && x <= 1050) || (x >= 800 && x <= 850) ||
            (x >= 600 && x <= 650) || (x >= 400 && x <= 450) ||
            (x >= 200 && x <= 250)) col--;
    if(y >= 1000) col += 8;

    curr = cvmGet(&matInfo, row,  col);
    if(value > curr)
        cvmSet(&matInfo, row, col, value);
}

// Depending on selected method, generate outfile name
QString InitMeasurement::getOutFile()
{
    QString outfile = "";

    if(method == 1) outfile= "./data/length_";
    else if(method == 2) outfile = "./data/area_";
    else if (method == 3) outfile = "./data/roundness_";
    else if (method == 4) outfile = "./data/height_";

    outfile = outfile.append(QString::number(seed) + "_");
    outfile = outfile.append(QString::number(replicate) + ".csv");

    return outfile;
}
